{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "7de19eeb",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pickle"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "927cb437",
   "metadata": {},
   "outputs": [],
   "source": [
    "with open('./train_X.pkl' , \"rb\" ) as file :\n",
    "    train_X = pickle.load(file)\n",
    "with open('./train_y.pkl' , \"rb\" ) as file :\n",
    "    train_y = pickle.load(file)\n",
    "with open('./test_X.pkl' , \"rb\" ) as file :\n",
    "    test_X = pickle.load(file)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7c5e5191",
   "metadata": {},
   "outputs": [],
   "source": [
    "train_y_converted = train_y - 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "65e4106e",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "3746882b",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e58d529c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>v2a1</th>\n",
       "      <th>hacdor</th>\n",
       "      <th>rooms</th>\n",
       "      <th>hacapo</th>\n",
       "      <th>v14a</th>\n",
       "      <th>refrig</th>\n",
       "      <th>v18q1</th>\n",
       "      <th>r4h1</th>\n",
       "      <th>r4h2</th>\n",
       "      <th>r4h3</th>\n",
       "      <th>...</th>\n",
       "      <th>lugar4</th>\n",
       "      <th>lugar5</th>\n",
       "      <th>lugar6</th>\n",
       "      <th>area2</th>\n",
       "      <th>age</th>\n",
       "      <th>edjefx</th>\n",
       "      <th>epared</th>\n",
       "      <th>etecho</th>\n",
       "      <th>eviv</th>\n",
       "      <th>instlevel</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>ID_279628684</th>\n",
       "      <td>190000.0</td>\n",
       "      <td>0</td>\n",
       "      <td>3</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>43</td>\n",
       "      <td>10</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ID_f29eb3ddd</th>\n",
       "      <td>135000.0</td>\n",
       "      <td>0</td>\n",
       "      <td>4</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>67</td>\n",
       "      <td>12</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>7</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ID_68de51c94</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0</td>\n",
       "      <td>8</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>92</td>\n",
       "      <td>11</td>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ID_ec05b1a7b</th>\n",
       "      <td>180000.0</td>\n",
       "      <td>0</td>\n",
       "      <td>5</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0</td>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>38</td>\n",
       "      <td>11</td>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "      <td>4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ID_1284f8aad</th>\n",
       "      <td>130000.0</td>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>30</td>\n",
       "      <td>9</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ID_18b0a845b</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0</td>\n",
       "      <td>4</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>26</td>\n",
       "      <td>5</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ID_a31274054</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0</td>\n",
       "      <td>3</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "      <td>4</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>40</td>\n",
       "      <td>2</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ID_32a00a8bf</th>\n",
       "      <td>46500.0</td>\n",
       "      <td>0</td>\n",
       "      <td>5</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>3</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>45</td>\n",
       "      <td>2</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ID_79d39dddc</th>\n",
       "      <td>0.0</td>\n",
       "      <td>0</td>\n",
       "      <td>3</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>67</td>\n",
       "      <td>0</td>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ID_d45ae367d</th>\n",
       "      <td>80000.0</td>\n",
       "      <td>0</td>\n",
       "      <td>6</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0</td>\n",
       "      <td>2</td>\n",
       "      <td>2</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>46</td>\n",
       "      <td>9</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>3</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>2973 rows × 94 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "                  v2a1  hacdor  rooms  hacapo  v14a  refrig  v18q1  r4h1  \\\n",
       "ID_279628684  190000.0       0      3       0     1       1    0.0     0   \n",
       "ID_f29eb3ddd  135000.0       0      4       0     1       1    1.0     0   \n",
       "ID_68de51c94       0.0       0      8       0     1       1    0.0     0   \n",
       "ID_ec05b1a7b  180000.0       0      5       0     1       1    1.0     0   \n",
       "ID_1284f8aad  130000.0       1      2       0     1       1    0.0     0   \n",
       "...                ...     ...    ...     ...   ...     ...    ...   ...   \n",
       "ID_18b0a845b       0.0       0      4       0     1       0    0.0     1   \n",
       "ID_a31274054       0.0       0      3       0     0       0    0.0     2   \n",
       "ID_32a00a8bf   46500.0       0      5       0     1       1    0.0     1   \n",
       "ID_79d39dddc       0.0       0      3       0     1       1    0.0     0   \n",
       "ID_d45ae367d   80000.0       0      6       0     1       1    0.0     0   \n",
       "\n",
       "              r4h2  r4h3  ...  lugar4  lugar5  lugar6  area2  age  edjefx  \\\n",
       "ID_279628684     1     1  ...       0       0       0      0   43      10   \n",
       "ID_f29eb3ddd     1     1  ...       0       0       0      0   67      12   \n",
       "ID_68de51c94     0     0  ...       0       0       0      0   92      11   \n",
       "ID_ec05b1a7b     2     2  ...       0       0       0      0   38      11   \n",
       "ID_1284f8aad     1     1  ...       0       0       0      0   30       9   \n",
       "...            ...   ...  ...     ...     ...     ...    ...  ...     ...   \n",
       "ID_18b0a845b     1     2  ...       0       0       1      1   26       5   \n",
       "ID_a31274054     2     4  ...       0       0       1      1   40       2   \n",
       "ID_32a00a8bf     2     3  ...       0       0       1      1   45       2   \n",
       "ID_79d39dddc     1     1  ...       0       0       1      1   67       0   \n",
       "ID_d45ae367d     2     2  ...       0       0       1      1   46       9   \n",
       "\n",
       "              epared  etecho  eviv  instlevel  \n",
       "ID_279628684       1       0     0          3  \n",
       "ID_f29eb3ddd       1       1     1          7  \n",
       "ID_68de51c94       1       2     2          4  \n",
       "ID_ec05b1a7b       2       2     2          4  \n",
       "ID_1284f8aad       0       0     1          3  \n",
       "...              ...     ...   ...        ...  \n",
       "ID_18b0a845b       1       1     1          1  \n",
       "ID_a31274054       1       0     1          1  \n",
       "ID_32a00a8bf       1       1     1          1  \n",
       "ID_79d39dddc       2       2     2          0  \n",
       "ID_d45ae367d       1       1     1          3  \n",
       "\n",
       "[2973 rows x 94 columns]"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "ename": "",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31mThe Kernel crashed while executing code in the current cell or a previous cell. \n",
      "\u001b[1;31mPlease review the code in the cell(s) to identify a possible cause of the failure. \n",
      "\u001b[1;31mClick <a href='https://aka.ms/vscodeJupyterKernelCrash'>here</a> for more info. \n",
      "\u001b[1;31mView Jupyter <a href='command:jupyter.viewOutput'>log</a> for further details."
     ]
    }
   ],
   "source": [
    "train_X"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "9364abb8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# lgb、catboost、xgb"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c12070c8",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "6b378cb1",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "c:\\ProgramData\\Anaconda3\\lib\\site-packages\\scipy\\__init__.py:155: UserWarning: A NumPy version >=1.18.5 and <1.25.0 is required for this version of SciPy (detected version 1.26.0\n",
      "  warnings.warn(f\"A NumPy version >={np_minversion} and <{np_maxversion}\"\n"
     ]
    }
   ],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import os\n",
    "import time\n",
    "import gc\n",
    "import matplotlib.pyplot as plt\n",
    "import pickle\n",
    "import lightgbm as lgb\n",
    "from sklearn.model_selection import StratifiedKFold, KFold\n",
    "from sklearn.metrics import roc_auc_score,f1_score\n",
    "from sklearn.decomposition import TruncatedSVD\n",
    "from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer\n",
    "from sklearn.preprocessing import LabelEncoder,OneHotEncoder\n",
    "from scipy import sparse\n",
    "import seaborn as sns\n",
    "from datetime import *\n",
    "from functools import reduce\n",
    "from sklearn.metrics import roc_curve\n",
    "from sklearn.model_selection import StratifiedKFold\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn import feature_selection\n",
    "import datetime\n",
    "import xgboost as xgb\n",
    "from sklearn.feature_selection import VarianceThreshold\n",
    "from catboost import CatBoostClassifier as cat\n",
    "import warnings\n",
    "warnings.filterwarnings('ignore')\n",
    "import optuna\n",
    "from sklearn.metrics import precision_score, recall_score,f1_score\n",
    "from lightgbm import *\n",
    "seed_list=[1993,2008,4096,1015]\n",
    "import random\n",
    "random.seed(5354)\n",
    "os.environ['PYTHONHASHSEED'] = str(5354)\n",
    "np.random.seed(5354)\n",
    "pd.set_option('display.max_info_columns', 500)\n",
    "pd.set_option('display.max_columns', 1000)\n",
    "pd.set_option('display.max_row', 300)\n",
    "pd.set_option('display.float_format', lambda x: ' %.5f' % x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "393614b5",
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.utils.class_weight import compute_class_weight"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "78c3006d",
   "metadata": {},
   "outputs": [],
   "source": [
    "def cv_model(clf, train_x, train_y, test_x, clf_name, seed=None, n_classes=None):\n",
    "    folds = 5\n",
    "    kf = StratifiedKFold(n_splits=folds, shuffle=True, random_state=seed)\n",
    "    \n",
    "    # 初始化oof和predict为二维数组（多分类）\n",
    "    oof = np.zeros((train_x.shape[0], n_classes))\n",
    "    predict = np.zeros((test_x.shape[0], n_classes))\n",
    "    df_feature_importance = pd.DataFrame()\n",
    "    cv_scores = []\n",
    "    \n",
    "    # 类别列处理\n",
    "    cols_list = train_x.columns.tolist()\n",
    "    cats_list = train_x.select_dtypes(include='category').columns\n",
    "    cats_list_idx = [i for i in range(len(cols_list)) if cols_list[i] in cats_list]\n",
    "    \n",
    "    # 类别编码处理\n",
    "    if clf_name in [\"xgb\", \"cat\"]:\n",
    "        for c in cats_list:\n",
    "            le = LabelEncoder()\n",
    "            train_x[c] = train_x[c].astype('str')\n",
    "            test_x[c] = test_x[c].astype('str')\n",
    "            lb_data = pd.concat([train_x[c], test_x[c]], axis=0)\n",
    "            le.fit(lb_data)\n",
    "            train_x[c] = le.transform(train_x[c])\n",
    "            test_x[c] = le.transform(test_x[c])\n",
    "    \n",
    "    # 处理无穷值\n",
    "    if clf_name == \"xgb\":\n",
    "        train_x.replace(np.inf, 9999999, inplace=True)\n",
    "        train_x.replace(-np.inf, -9999999, inplace=True)\n",
    "        test_x.replace(np.inf, 9999999, inplace=True)\n",
    "        test_x.replace(-np.inf, -9999999, inplace=True)\n",
    "    \n",
    "    for i, (train_index, valid_index) in enumerate(kf.split(train_x, train_y)):\n",
    "        print('************************************ {} ************************************'.format(str(i+1)))\n",
    "        trn_x, trn_y, val_x, val_y = train_x.iloc[train_index], train_y[train_index], train_x.iloc[valid_index], train_y[valid_index]\n",
    "\n",
    "        if clf_name == \"lgb\":\n",
    "            # 多分类参数设置 - 添加 is_unbalance=True实现class_weight='balanced'\n",
    "            params = {\n",
    "                'boost_from_average': 'false',\n",
    "                'boost': 'gbdt',\n",
    "                'metric': 'multi_logloss',\n",
    "                'num_class': n_classes,\n",
    "                'max_depth': 7,\n",
    "                'num_leaves': 2**7 - 1,\n",
    "                'objective': 'multiclass',\n",
    "                'min_child_weight': 16,\n",
    "                'min_data_in_leaf': 6,\n",
    "                'min_split_gain': 0.7,\n",
    "                'bagging_fraction': 0.82,\n",
    "                'feature_fraction': 0.74,\n",
    "                'reg_lambda': 2.65,\n",
    "                'reg_alpha': 3.85,\n",
    "                'bagging_freq': 1,\n",
    "                'seed': seed,\n",
    "                'nthread': 8,\n",
    "                'n_jobs': 8,\n",
    "                'verbose': -1,\n",
    "                'is_unbalance': True  # 添加class_weight='balanced'等效参数\n",
    "            }\n",
    "            \n",
    "            train_matrix = clf.Dataset(trn_x, label=trn_y)\n",
    "            valid_matrix = clf.Dataset(val_x, label=val_y)\n",
    "            callbacks = [log_evaluation(period=100), early_stopping(stopping_rounds=500)]\n",
    "            \n",
    "            model = clf.train(params, train_matrix, num_boost_round=20000, \n",
    "                             valid_sets=[train_matrix, valid_matrix], \n",
    "                             callbacks=callbacks)\n",
    "            \n",
    "            val_pred = model.predict(val_x, num_iteration=model.best_iteration)\n",
    "            test_pred = model.predict(test_x, num_iteration=model.best_iteration)\n",
    "            \n",
    "            # 特征重要性\n",
    "            df_fold_importance = pd.DataFrame()\n",
    "            df_fold_importance['feature'] = train_x.columns\n",
    "            df_fold_importance['importance'] = model.feature_importance()\n",
    "            df_fold_importance['fold'] = i + 1\n",
    "            df_feature_importance = pd.concat([df_feature_importance, df_fold_importance], axis=0)\n",
    "\n",
    "        elif clf_name == \"xgb\":\n",
    "            # 计算样本权重实现class_weight='balanced'\n",
    "            class_weights = compute_class_weight('balanced', classes=np.unique(trn_y), y=trn_y)\n",
    "            sample_weights = np.array([class_weights[i] for i in trn_y])\n",
    "            \n",
    "            # 多分类参数设置\n",
    "            params = {\n",
    "                'booster': 'gbtree',\n",
    "                'objective': 'multi:softprob',\n",
    "                'eval_metric': 'mlogloss',\n",
    "                'num_class': n_classes,\n",
    "                'gamma': 1,\n",
    "                'min_child_weight': 1.5,\n",
    "                'max_depth': 7,\n",
    "                'lambda': 10,\n",
    "                'subsample': 0.7,\n",
    "                'colsample_bytree': 0.7,\n",
    "                'colsample_bylevel': 0.7,\n",
    "                'eta': 0.05,\n",
    "                'tree_method': 'exact',\n",
    "                'seed': seed,\n",
    "                'nthread': 8\n",
    "            }\n",
    "            \n",
    "            train_matrix = clf.DMatrix(trn_x, label=trn_y, weight=sample_weights)  # 添加样本权重\n",
    "            valid_matrix = clf.DMatrix(val_x, label=val_y)\n",
    "            test_matrix = clf.DMatrix(test_x)\n",
    "            \n",
    "            watchlist = [(train_matrix, 'train'), (valid_matrix, 'eval')]\n",
    "            \n",
    "            model = clf.train(params, train_matrix, num_boost_round=20000, \n",
    "                             evals=watchlist, verbose_eval=100, \n",
    "                             early_stopping_rounds=500)\n",
    "            \n",
    "            val_pred = model.predict(valid_matrix)\n",
    "            test_pred = model.predict(test_matrix)\n",
    "            \n",
    "            # 特征重要性\n",
    "            df_fold_importance = pd.DataFrame()\n",
    "            scores = model.get_score(importance_type='gain')\n",
    "            if scores:\n",
    "                df_fold_importance['feature'] = scores.keys()\n",
    "                df_fold_importance['importance'] = scores.values()\n",
    "                df_fold_importance['fold'] = i + 1\n",
    "                df_feature_importance = pd.concat([df_feature_importance, df_fold_importance], axis=0)\n",
    "\n",
    "        elif clf_name == \"cat\":\n",
    "            # 多分类参数设置 - 添加auto_class_weights='Balanced'\n",
    "            model = clf(\n",
    "                iterations=10000,\n",
    "                random_seed=seed,\n",
    "                loss_function='MultiClass',\n",
    "                eval_metric='MultiClass',\n",
    "                learning_rate=0.01,\n",
    "                max_depth=5,\n",
    "                early_stopping_rounds=200,\n",
    "                metric_period=100,\n",
    "                l2_leaf_reg=3,\n",
    "                task_type='CPU',\n",
    "                auto_class_weights='Balanced'  # 添加class_weight='balanced'\n",
    "            )\n",
    "            \n",
    "            model.fit(trn_x, trn_y, eval_set=(val_x, val_y),\n",
    "                      use_best_model=True, cat_features=cats_list_idx,\n",
    "                      verbose=1)\n",
    "            \n",
    "            val_pred = model.predict_proba(val_x)\n",
    "            test_pred = model.predict_proba(test_x)\n",
    "            \n",
    "            # 特征重要性\n",
    "            df_fold_importance = pd.DataFrame()\n",
    "            df_fold_importance['feature'] = train_x.columns\n",
    "            df_fold_importance['importance'] = model.feature_importances_\n",
    "            df_fold_importance['fold'] = i + 1\n",
    "            df_feature_importance = pd.concat([df_feature_importance, df_fold_importance], axis=0)\n",
    "        \n",
    "        # 存储预测概率\n",
    "        oof[valid_index] = val_pred\n",
    "        predict += test_pred / folds\n",
    "        \n",
    "        # 计算macro F1\n",
    "        val_pred_labels = np.argmax(val_pred, axis=1)\n",
    "        fold_f1 = f1_score(val_y, val_pred_labels, average='macro')\n",
    "        cv_scores.append(fold_f1)\n",
    "        print('Macro F1 CV score: {:<8.5f}'.format(fold_f1))\n",
    "    \n",
    "    mean_cv_f1 = np.mean(cv_scores)\n",
    "    print(\"平均 Macro F1:::\", mean_cv_f1)\n",
    "    return oof, predict, model, df_feature_importance, mean_cv_f1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "6b50c858",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "训练 LightGBM 模型\n"
     ]
    },
    {
     "ename": "NameError",
     "evalue": "name 'seed' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m~\\AppData\\Local\\Temp\\ipykernel_24428\\1794386967.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      3\u001b[0m lgb_oof, lgb_test_probs, lgb_model, lgb_feat_importance, lgb_f1 = cv_model(\n\u001b[0;32m      4\u001b[0m     \u001b[0mlgb\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mtrain_X\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcopy\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mtrain_y_converted\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mtest_X\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcopy\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 5\u001b[1;33m     \u001b[1;34m\"lgb\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mseed\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mseed\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mn_classes\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mn_classes\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      6\u001b[0m )\n\u001b[0;32m      7\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34mf\"LightGBM 模型训练完成，平均 Macro F1: {lgb_f1:.5f}\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mNameError\u001b[0m: name 'seed' is not defined"
     ]
    }
   ],
   "source": [
    "# 训练三个模型并收集预测结果\n",
    "print(\"\\n训练 LightGBM 模型\")\n",
    "lgb_oof, lgb_test_probs, lgb_model, lgb_feat_importance, lgb_f1 = cv_model(\n",
    "    lgb, train_X.copy(), train_y_converted, test_X.copy(), \n",
    "    \"lgb\", seed=seed, n_classes=n_classes\n",
    ")\n",
    "print(f\"LightGBM 模型训练完成，平均 Macro F1: {lgb_f1:.5f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "232d462d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "训练 XGBoost 模型\n",
      "************************************ 1 ************************************\n",
      "[0]\ttrain-mlogloss:1.36688\teval-mlogloss:1.36978\n",
      "[100]\ttrain-mlogloss:0.71689\teval-mlogloss:1.01250\n",
      "[200]\ttrain-mlogloss:0.53928\teval-mlogloss:0.97730\n",
      "[300]\ttrain-mlogloss:0.45498\teval-mlogloss:0.96811\n",
      "[400]\ttrain-mlogloss:0.40773\teval-mlogloss:0.96703\n",
      "[500]\ttrain-mlogloss:0.37930\teval-mlogloss:0.96426\n",
      "[600]\ttrain-mlogloss:0.35964\teval-mlogloss:0.96514\n",
      "[700]\ttrain-mlogloss:0.34571\teval-mlogloss:0.96731\n",
      "[800]\ttrain-mlogloss:0.33442\teval-mlogloss:0.96751\n",
      "[900]\ttrain-mlogloss:0.32673\teval-mlogloss:0.96700\n",
      "[1000]\ttrain-mlogloss:0.31918\teval-mlogloss:0.96901\n",
      "[1009]\ttrain-mlogloss:0.31838\teval-mlogloss:0.96864\n",
      "Macro F1 CV score: 0.38231 \n",
      "************************************ 2 ************************************\n",
      "[0]\ttrain-mlogloss:1.36831\teval-mlogloss:1.36986\n",
      "[100]\ttrain-mlogloss:0.72688\teval-mlogloss:0.97833\n",
      "[200]\ttrain-mlogloss:0.54507\teval-mlogloss:0.94077\n",
      "[300]\ttrain-mlogloss:0.45903\teval-mlogloss:0.93015\n",
      "[400]\ttrain-mlogloss:0.41223\teval-mlogloss:0.92329\n",
      "[500]\ttrain-mlogloss:0.38373\teval-mlogloss:0.92075\n",
      "[600]\ttrain-mlogloss:0.36504\teval-mlogloss:0.91942\n",
      "[700]\ttrain-mlogloss:0.35111\teval-mlogloss:0.91846\n",
      "[800]\ttrain-mlogloss:0.33939\teval-mlogloss:0.91739\n",
      "[900]\ttrain-mlogloss:0.33040\teval-mlogloss:0.91576\n",
      "[1000]\ttrain-mlogloss:0.32286\teval-mlogloss:0.91575\n",
      "[1100]\ttrain-mlogloss:0.31703\teval-mlogloss:0.91577\n",
      "[1200]\ttrain-mlogloss:0.31200\teval-mlogloss:0.91426\n",
      "[1300]\ttrain-mlogloss:0.30799\teval-mlogloss:0.91444\n",
      "[1400]\ttrain-mlogloss:0.30425\teval-mlogloss:0.91471\n",
      "[1500]\ttrain-mlogloss:0.30078\teval-mlogloss:0.91412\n",
      "[1600]\ttrain-mlogloss:0.29732\teval-mlogloss:0.91392\n",
      "[1700]\ttrain-mlogloss:0.29416\teval-mlogloss:0.91407\n",
      "[1800]\ttrain-mlogloss:0.29233\teval-mlogloss:0.91447\n",
      "[1900]\ttrain-mlogloss:0.29060\teval-mlogloss:0.91369\n",
      "[2000]\ttrain-mlogloss:0.28815\teval-mlogloss:0.91449\n",
      "[2100]\ttrain-mlogloss:0.28626\teval-mlogloss:0.91399\n",
      "[2200]\ttrain-mlogloss:0.28445\teval-mlogloss:0.91355\n",
      "[2300]\ttrain-mlogloss:0.28271\teval-mlogloss:0.91314\n",
      "[2400]\ttrain-mlogloss:0.28116\teval-mlogloss:0.91326\n",
      "[2500]\ttrain-mlogloss:0.27988\teval-mlogloss:0.91336\n",
      "[2600]\ttrain-mlogloss:0.27850\teval-mlogloss:0.91290\n",
      "[2700]\ttrain-mlogloss:0.27662\teval-mlogloss:0.91341\n",
      "[2800]\ttrain-mlogloss:0.27549\teval-mlogloss:0.91320\n",
      "[2900]\ttrain-mlogloss:0.27409\teval-mlogloss:0.91367\n",
      "[3000]\ttrain-mlogloss:0.27263\teval-mlogloss:0.91441\n",
      "[3100]\ttrain-mlogloss:0.27153\teval-mlogloss:0.91580\n",
      "[3158]\ttrain-mlogloss:0.27093\teval-mlogloss:0.91510\n",
      "Macro F1 CV score: 0.43580 \n",
      "************************************ 3 ************************************\n",
      "[0]\ttrain-mlogloss:1.36884\teval-mlogloss:1.36786\n",
      "[100]\ttrain-mlogloss:0.72051\teval-mlogloss:0.98286\n",
      "[200]\ttrain-mlogloss:0.54161\teval-mlogloss:0.95755\n",
      "[300]\ttrain-mlogloss:0.45659\teval-mlogloss:0.95016\n",
      "[400]\ttrain-mlogloss:0.40840\teval-mlogloss:0.94725\n",
      "[500]\ttrain-mlogloss:0.37979\teval-mlogloss:0.94635\n",
      "[600]\ttrain-mlogloss:0.35934\teval-mlogloss:0.94830\n",
      "[700]\ttrain-mlogloss:0.34639\teval-mlogloss:0.94920\n",
      "[800]\ttrain-mlogloss:0.33588\teval-mlogloss:0.95034\n",
      "[873]\ttrain-mlogloss:0.32962\teval-mlogloss:0.95114\n",
      "Macro F1 CV score: 0.41606 \n",
      "************************************ 4 ************************************\n",
      "[0]\ttrain-mlogloss:1.36896\teval-mlogloss:1.37021\n",
      "[100]\ttrain-mlogloss:0.72426\teval-mlogloss:0.98627\n",
      "[200]\ttrain-mlogloss:0.53866\teval-mlogloss:0.95722\n",
      "[300]\ttrain-mlogloss:0.45656\teval-mlogloss:0.95104\n",
      "[400]\ttrain-mlogloss:0.41174\teval-mlogloss:0.94797\n",
      "[500]\ttrain-mlogloss:0.38222\teval-mlogloss:0.95008\n",
      "[600]\ttrain-mlogloss:0.36132\teval-mlogloss:0.95038\n",
      "[700]\ttrain-mlogloss:0.34868\teval-mlogloss:0.94917\n",
      "[800]\ttrain-mlogloss:0.33755\teval-mlogloss:0.95015\n",
      "[900]\ttrain-mlogloss:0.32902\teval-mlogloss:0.94957\n",
      "[914]\ttrain-mlogloss:0.32756\teval-mlogloss:0.95062\n",
      "Macro F1 CV score: 0.46264 \n",
      "************************************ 5 ************************************\n",
      "[0]\ttrain-mlogloss:1.36933\teval-mlogloss:1.37056\n",
      "[100]\ttrain-mlogloss:0.72011\teval-mlogloss:0.99870\n",
      "[200]\ttrain-mlogloss:0.53726\teval-mlogloss:0.98372\n",
      "[300]\ttrain-mlogloss:0.45394\teval-mlogloss:0.98137\n",
      "[400]\ttrain-mlogloss:0.40846\teval-mlogloss:0.98494\n",
      "[500]\ttrain-mlogloss:0.37906\teval-mlogloss:0.98428\n",
      "[600]\ttrain-mlogloss:0.35947\teval-mlogloss:0.98804\n",
      "[700]\ttrain-mlogloss:0.34620\teval-mlogloss:0.98930\n",
      "[754]\ttrain-mlogloss:0.34048\teval-mlogloss:0.99115\n",
      "Macro F1 CV score: 0.38634 \n",
      "平均 Macro F1::: 0.41662843566819296\n",
      "XGBoost 模型训练完成，平均 Macro F1: 0.41663\n"
     ]
    }
   ],
   "source": [
    "print(\"\\n训练 XGBoost 模型\")\n",
    "xgb_oof, xgb_test_probs, xgb_model, xgb_feat_importance, xgb_f1 = cv_model(\n",
    "    xgb, train_X.copy(), train_y_converted, test_X.copy(), \n",
    "    \"xgb\", seed=seed, n_classes=n_classes\n",
    ")\n",
    "print(f\"XGBoost 模型训练完成，平均 Macro F1: {xgb_f1:.5f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "f934c779",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "训练 CatBoost 模型\n",
      "************************************ 1 ************************************\n",
      "0:\tlearn: 1.3846515\ttest: 1.3852018\tbest: 1.3852018 (0)\ttotal: 3.37ms\tremaining: 33.7s\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Warning: Overfitting detector is active, thus evaluation metric is calculated on every iteration. 'metric_period' is ignored for evaluation metric.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "100:\tlearn: 1.2485716\ttest: 1.3002742\tbest: 1.3002742 (100)\ttotal: 284ms\tremaining: 27.8s\n",
      "200:\tlearn: 1.1772620\ttest: 1.2645769\tbest: 1.2645769 (200)\ttotal: 587ms\tremaining: 28.6s\n",
      "300:\tlearn: 1.1277213\ttest: 1.2477240\tbest: 1.2477240 (300)\ttotal: 864ms\tremaining: 27.8s\n",
      "400:\tlearn: 1.0894719\ttest: 1.2378997\tbest: 1.2378094 (397)\ttotal: 1.16s\tremaining: 27.7s\n",
      "500:\tlearn: 1.0609646\ttest: 1.2341342\tbest: 1.2340232 (477)\ttotal: 1.43s\tremaining: 27.1s\n",
      "600:\tlearn: 1.0338498\ttest: 1.2323544\tbest: 1.2322787 (578)\ttotal: 1.7s\tremaining: 26.7s\n",
      "700:\tlearn: 1.0078073\ttest: 1.2312311\tbest: 1.2312311 (700)\ttotal: 1.97s\tremaining: 26.2s\n",
      "800:\tlearn: 0.9808076\ttest: 1.2293195\tbest: 1.2290958 (780)\ttotal: 2.28s\tremaining: 26.2s\n",
      "900:\tlearn: 0.9540747\ttest: 1.2298590\tbest: 1.2290722 (826)\ttotal: 2.54s\tremaining: 25.7s\n",
      "1000:\tlearn: 0.9249009\ttest: 1.2289446\tbest: 1.2288100 (995)\ttotal: 2.82s\tremaining: 25.3s\n",
      "1100:\tlearn: 0.8971043\ttest: 1.2307091\tbest: 1.2287958 (1015)\ttotal: 3.13s\tremaining: 25.3s\n",
      "1200:\tlearn: 0.8705016\ttest: 1.2327926\tbest: 1.2287958 (1015)\ttotal: 3.4s\tremaining: 24.9s\n",
      "Stopped by overfitting detector  (200 iterations wait)\n",
      "\n",
      "bestTest = 1.228795815\n",
      "bestIteration = 1015\n",
      "\n",
      "Shrink model to first 1016 iterations.\n",
      "Macro F1 CV score: 0.40859 \n",
      "************************************ 2 ************************************\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Warning: Overfitting detector is active, thus evaluation metric is calculated on every iteration. 'metric_period' is ignored for evaluation metric.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0:\tlearn: 1.3841399\ttest: 1.3843568\tbest: 1.3843568 (0)\ttotal: 4.02ms\tremaining: 40.2s\n",
      "100:\tlearn: 1.2537231\ttest: 1.2793780\tbest: 1.2793780 (100)\ttotal: 317ms\tremaining: 31.1s\n",
      "200:\tlearn: 1.1846639\ttest: 1.2342999\tbest: 1.2342999 (200)\ttotal: 611ms\tremaining: 29.8s\n",
      "300:\tlearn: 1.1405547\ttest: 1.2115588\tbest: 1.2115588 (300)\ttotal: 876ms\tremaining: 28.2s\n",
      "400:\tlearn: 1.1062956\ttest: 1.1937438\tbest: 1.1937438 (400)\ttotal: 1.14s\tremaining: 27.3s\n",
      "500:\tlearn: 1.0775242\ttest: 1.1829009\tbest: 1.1829009 (500)\ttotal: 1.41s\tremaining: 26.7s\n",
      "600:\tlearn: 1.0504968\ttest: 1.1747733\tbest: 1.1747733 (600)\ttotal: 1.68s\tremaining: 26.2s\n",
      "700:\tlearn: 1.0232417\ttest: 1.1682378\tbest: 1.1682378 (700)\ttotal: 1.93s\tremaining: 25.7s\n",
      "800:\tlearn: 0.9984656\ttest: 1.1637306\tbest: 1.1636611 (799)\ttotal: 2.21s\tremaining: 25.4s\n",
      "900:\tlearn: 0.9712808\ttest: 1.1603971\tbest: 1.1603534 (898)\ttotal: 2.49s\tremaining: 25.1s\n",
      "1000:\tlearn: 0.9442087\ttest: 1.1578698\tbest: 1.1578345 (998)\ttotal: 2.78s\tremaining: 25s\n",
      "1100:\tlearn: 0.9147520\ttest: 1.1556792\tbest: 1.1556792 (1100)\ttotal: 3.05s\tremaining: 24.6s\n",
      "1200:\tlearn: 0.8865868\ttest: 1.1536033\tbest: 1.1534479 (1190)\ttotal: 3.32s\tremaining: 24.3s\n",
      "1300:\tlearn: 0.8611677\ttest: 1.1525167\tbest: 1.1523643 (1295)\ttotal: 3.59s\tremaining: 24s\n",
      "1400:\tlearn: 0.8367830\ttest: 1.1532080\tbest: 1.1523643 (1295)\ttotal: 3.88s\tremaining: 23.8s\n",
      "Stopped by overfitting detector  (200 iterations wait)\n",
      "\n",
      "bestTest = 1.152364257\n",
      "bestIteration = 1295\n",
      "\n",
      "Shrink model to first 1296 iterations.\n",
      "Macro F1 CV score: 0.44141 \n",
      "************************************ 3 ************************************\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Warning: Overfitting detector is active, thus evaluation metric is calculated on every iteration. 'metric_period' is ignored for evaluation metric.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0:\tlearn: 1.3845858\ttest: 1.3847845\tbest: 1.3847845 (0)\ttotal: 3.49ms\tremaining: 34.9s\n",
      "100:\tlearn: 1.2571136\ttest: 1.2810164\tbest: 1.2810164 (100)\ttotal: 348ms\tremaining: 34.1s\n",
      "200:\tlearn: 1.1890021\ttest: 1.2380284\tbest: 1.2380284 (200)\ttotal: 620ms\tremaining: 30.2s\n",
      "300:\tlearn: 1.1405573\ttest: 1.2151792\tbest: 1.2151792 (300)\ttotal: 889ms\tremaining: 28.7s\n",
      "400:\tlearn: 1.1040015\ttest: 1.2020491\tbest: 1.2020491 (400)\ttotal: 1.18s\tremaining: 28.1s\n",
      "500:\tlearn: 1.0723053\ttest: 1.1942090\tbest: 1.1942090 (500)\ttotal: 1.44s\tremaining: 27.3s\n",
      "600:\tlearn: 1.0454923\ttest: 1.1875573\tbest: 1.1875573 (600)\ttotal: 1.71s\tremaining: 26.8s\n",
      "700:\tlearn: 1.0195521\ttest: 1.1838364\tbest: 1.1838186 (690)\ttotal: 1.98s\tremaining: 26.3s\n",
      "800:\tlearn: 0.9956619\ttest: 1.1811655\tbest: 1.1811327 (797)\ttotal: 2.27s\tremaining: 26.1s\n",
      "900:\tlearn: 0.9677203\ttest: 1.1800128\tbest: 1.1798144 (895)\ttotal: 2.56s\tremaining: 25.8s\n",
      "1000:\tlearn: 0.9382194\ttest: 1.1788501\tbest: 1.1786053 (995)\ttotal: 2.83s\tremaining: 25.5s\n",
      "1100:\tlearn: 0.9091510\ttest: 1.1791595\tbest: 1.1783298 (1060)\ttotal: 3.11s\tremaining: 25.1s\n",
      "1200:\tlearn: 0.8812660\ttest: 1.1799831\tbest: 1.1783298 (1060)\ttotal: 3.38s\tremaining: 24.7s\n",
      "Stopped by overfitting detector  (200 iterations wait)\n",
      "\n",
      "bestTest = 1.17832984\n",
      "bestIteration = 1060\n",
      "\n",
      "Shrink model to first 1061 iterations.\n",
      "Macro F1 CV score: 0.43147 \n",
      "************************************ 4 ************************************\n",
      "0:\tlearn: 1.3839659\ttest: 1.3838316\tbest: 1.3838316 (0)\ttotal: 3.37ms\tremaining: 33.7s\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Warning: Overfitting detector is active, thus evaluation metric is calculated on every iteration. 'metric_period' is ignored for evaluation metric.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "100:\tlearn: 1.2604506\ttest: 1.2792833\tbest: 1.2792833 (100)\ttotal: 331ms\tremaining: 32.5s\n",
      "200:\tlearn: 1.1920964\ttest: 1.2342193\tbest: 1.2342193 (200)\ttotal: 598ms\tremaining: 29.2s\n",
      "300:\tlearn: 1.1429406\ttest: 1.2078141\tbest: 1.2078141 (300)\ttotal: 861ms\tremaining: 27.7s\n",
      "400:\tlearn: 1.1061793\ttest: 1.1938473\tbest: 1.1938473 (400)\ttotal: 1.12s\tremaining: 26.8s\n",
      "500:\tlearn: 1.0765069\ttest: 1.1846074\tbest: 1.1845844 (499)\ttotal: 1.39s\tremaining: 26.4s\n",
      "600:\tlearn: 1.0484797\ttest: 1.1772964\tbest: 1.1772964 (600)\ttotal: 1.67s\tremaining: 26.1s\n",
      "700:\tlearn: 1.0220734\ttest: 1.1734245\tbest: 1.1734245 (700)\ttotal: 1.93s\tremaining: 25.6s\n",
      "800:\tlearn: 0.9958381\ttest: 1.1706401\tbest: 1.1706401 (800)\ttotal: 2.2s\tremaining: 25.2s\n",
      "900:\tlearn: 0.9675803\ttest: 1.1685041\tbest: 1.1684724 (899)\ttotal: 2.47s\tremaining: 24.9s\n",
      "1000:\tlearn: 0.9380833\ttest: 1.1679645\tbest: 1.1679389 (999)\ttotal: 2.73s\tremaining: 24.5s\n",
      "1100:\tlearn: 0.9087201\ttest: 1.1676872\tbest: 1.1672979 (1086)\ttotal: 2.99s\tremaining: 24.2s\n",
      "1200:\tlearn: 0.8808024\ttest: 1.1675707\tbest: 1.1672979 (1086)\ttotal: 3.27s\tremaining: 24s\n",
      "Stopped by overfitting detector  (200 iterations wait)\n",
      "\n",
      "bestTest = 1.167297911\n",
      "bestIteration = 1086\n",
      "\n",
      "Shrink model to first 1087 iterations.\n",
      "Macro F1 CV score: 0.42638 \n",
      "************************************ 5 ************************************\n",
      "0:\tlearn: 1.3839408\ttest: 1.3841524\tbest: 1.3841524 (0)\ttotal: 3.1ms\tremaining: 31s\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Warning: Overfitting detector is active, thus evaluation metric is calculated on every iteration. 'metric_period' is ignored for evaluation metric.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "100:\tlearn: 1.2538717\ttest: 1.2874031\tbest: 1.2874031 (100)\ttotal: 322ms\tremaining: 31.5s\n",
      "200:\tlearn: 1.1818017\ttest: 1.2457955\tbest: 1.2457955 (200)\ttotal: 593ms\tremaining: 28.9s\n",
      "300:\tlearn: 1.1353776\ttest: 1.2246201\tbest: 1.2246201 (300)\ttotal: 859ms\tremaining: 27.7s\n",
      "400:\tlearn: 1.0993966\ttest: 1.2111863\tbest: 1.2111863 (400)\ttotal: 1.14s\tremaining: 27.2s\n",
      "500:\tlearn: 1.0701476\ttest: 1.2021768\tbest: 1.2021768 (500)\ttotal: 1.4s\tremaining: 26.6s\n",
      "600:\tlearn: 1.0424353\ttest: 1.1958690\tbest: 1.1957587 (599)\ttotal: 1.67s\tremaining: 26.2s\n",
      "700:\tlearn: 1.0165987\ttest: 1.1909941\tbest: 1.1909941 (700)\ttotal: 1.97s\tremaining: 26.1s\n",
      "800:\tlearn: 0.9904360\ttest: 1.1890043\tbest: 1.1889521 (792)\ttotal: 2.23s\tremaining: 25.6s\n",
      "900:\tlearn: 0.9612374\ttest: 1.1870388\tbest: 1.1867510 (889)\ttotal: 2.51s\tremaining: 25.4s\n",
      "1000:\tlearn: 0.9321228\ttest: 1.1868149\tbest: 1.1862873 (952)\ttotal: 2.78s\tremaining: 25s\n",
      "1100:\tlearn: 0.9040355\ttest: 1.1873799\tbest: 1.1862873 (952)\ttotal: 3.05s\tremaining: 24.6s\n",
      "Stopped by overfitting detector  (200 iterations wait)\n",
      "\n",
      "bestTest = 1.186287251\n",
      "bestIteration = 952\n",
      "\n",
      "Shrink model to first 953 iterations.\n",
      "Macro F1 CV score: 0.40763 \n",
      "平均 Macro F1::: 0.42309710783539456\n",
      "CatBoost 模型训练完成，平均 Macro F1: 0.42310\n"
     ]
    }
   ],
   "source": [
    "print(\"\\n训练 CatBoost 模型\")\n",
    "cat_oof, cat_test_probs, cat_model, cat_feat_importance, cat_f1 = cv_model(\n",
    "    cat, train_X.copy(), train_y_converted, test_X.copy(), \n",
    "    \"cat\", seed=seed, n_classes=n_classes\n",
    ")\n",
    "print(f\"CatBoost 模型训练完成，平均 Macro F1: {cat_f1:.5f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "236c4836",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "为融合模型优化阈值...\n",
      "最优阈值: [1.00891178 1.05592622 0.99153311 1.02491318]\n"
     ]
    }
   ],
   "source": [
    "# 模型融合 (1:1:1)\n",
    "fused_oof = (lgb_oof + xgb_oof + cat_oof) / 3\n",
    "fused_test_probs = (lgb_test_probs + xgb_test_probs + cat_test_probs) / 3\n",
    "\n",
    "# 优化阈值函数\n",
    "def optimize_thresholds(thresholds):\n",
    "    adjusted_probs = fused_oof / thresholds\n",
    "    preds = np.argmax(adjusted_probs, axis=1)\n",
    "    return -f1_score(train_y_converted, preds, average='macro')\n",
    "\n",
    "# 优化阈值\n",
    "print(\"\\n为融合模型优化阈值...\")\n",
    "initial_thresholds = np.ones(n_classes)\n",
    "result = minimize(optimize_thresholds, initial_thresholds, \n",
    "                  method='Nelder-Mead', \n",
    "                  bounds=[(0.1, 10)] * n_classes)\n",
    "\n",
    "best_thresholds = result.x\n",
    "print(f\"最优阈值: {best_thresholds}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "627e7203",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "融合模型使用最优阈值的 Macro F1: 0.42919\n"
     ]
    }
   ],
   "source": [
    "# 应用最优阈值\n",
    "adjusted_probs = fused_oof / best_thresholds\n",
    "best_preds_converted = np.argmax(adjusted_probs, axis=1)\n",
    "best_preds = best_preds_converted + 1\n",
    "best_f1 = f1_score(train_y, best_preds, average='macro')\n",
    "print(f\"融合模型使用最优阈值的 Macro F1: {best_f1:.5f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "e501281d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 测试集预测\n",
    "adjusted_test_probs = fused_test_probs / best_thresholds\n",
    "test_preds_optimized_converted = np.argmax(adjusted_test_probs, axis=1)\n",
    "test_preds_optimized = test_preds_optimized_converted + 1\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "9d254267",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "模型性能总结:\n",
      "LightGBM: 平均 Macro F1 = 0.35500\n",
      "XGBoost: 平均 Macro F1 = 0.41663\n",
      "CatBoost: 平均 Macro F1 = 0.42310\n",
      "融合模型: Macro F1 = 0.42919\n",
      "\n",
      "预测分布:\n",
      "4    15764\n",
      "2     3659\n",
      "1     2292\n",
      "3     2141\n",
      "Name: Target, dtype: int64\n",
      "\n",
      "提交文件已保存为: submission_fused_1013_150110.csv\n"
     ]
    }
   ],
   "source": [
    "# 保存结果\n",
    "submission = pd.read_csv(\"./test.csv\")[['Id']]\n",
    "submission['Target'] = test_preds_optimized\n",
    "\n",
    "# 输出模型性能\n",
    "print(\"\\n模型性能总结:\")\n",
    "print(f\"LightGBM: 平均 Macro F1 = {lgb_f1:.5f}\")\n",
    "print(f\"XGBoost: 平均 Macro F1 = {xgb_f1:.5f}\")\n",
    "print(f\"CatBoost: 平均 Macro F1 = {cat_f1:.5f}\")\n",
    "print(f\"融合模型: Macro F1 = {best_f1:.5f}\")\n",
    "\n",
    "# 输出预测分布\n",
    "print(\"\\n预测分布:\")\n",
    "print(submission['Target'].value_counts())\n",
    "\n",
    "# 保存结果\n",
    "from datetime import datetime\n",
    "current_time = datetime.now().strftime(\"%m%d_%H%M%S\")\n",
    "filename = f'submission_fused_{current_time}.csv'\n",
    "submission.to_csv(filename, index=False)\n",
    "print(f\"\\n提交文件已保存为: {filename}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8adc5332",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1a7cd7f7",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b2ab2e57",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 遍历最优解"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "703fdc2f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "开始自动权重搜索...\n",
      "最优权重: LightGBM=0.15, XGBoost=0.00, CatBoost=0.85\n",
      "使用最优权重的初始Macro F1: 0.43533\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "from scipy.optimize import minimize\n",
    "from sklearn.metrics import f1_score\n",
    "\n",
    "# 假设你已经有了以下变量：\n",
    "# lgb_oof, xgb_oof, cat_oof - 三个模型的OOF预测概率\n",
    "# lgb_test_probs, xgb_test_probs, cat_test_probs - 三个模型的测试集预测概率\n",
    "# train_y - 训练集真实标签（格式为1,2,3,4）\n",
    "# n_classes - 类别数量\n",
    "\n",
    "# 将标签转换为0-indexed格式\n",
    "train_y_converted = train_y - 1\n",
    "\n",
    "# 1. 自动权重搜索法（在验证集上寻找最优权重）\n",
    "print(\"开始自动权重搜索...\")\n",
    "best_score = -np.inf\n",
    "best_weights = None\n",
    "\n",
    "# 网格搜索所有可能的权重组合（步长0.05）\n",
    "for w1 in np.arange(0, 1.05, 0.05):\n",
    "    for w2 in np.arange(0, 1.05 - w1, 0.05):\n",
    "        w3 = 1 - w1 - w2\n",
    "        if w3 < 0:  # 确保权重和为1\n",
    "            continue\n",
    "            \n",
    "        # 使用当前权重融合OOF预测\n",
    "        fused_oof = w1 * lgb_oof + w2 * xgb_oof + w3 * cat_oof\n",
    "        \n",
    "        # 使用默认阈值（argmax）计算F1分数\n",
    "        preds = np.argmax(fused_oof, axis=1)\n",
    "        score = f1_score(train_y_converted, preds, average='macro')\n",
    "        \n",
    "        # 更新最佳权重\n",
    "        if score > best_score:\n",
    "            best_score = score\n",
    "            best_weights = (w1, w2, w3)\n",
    "\n",
    "print(f\"最优权重: LightGBM={best_weights[0]:.2f}, XGBoost={best_weights[1]:.2f}, CatBoost={best_weights[2]:.2f}\")\n",
    "print(f\"使用最优权重的初始Macro F1: {best_score:.5f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "id": "c494c739",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "为融合模型优化阈值...\n",
      "最优阈值: [1.05845644 1.00491995 0.99601013 0.9909764 ]\n"
     ]
    }
   ],
   "source": [
    "# 2. 使用最优权重融合OOF和测试集预测\n",
    "fused_oof = best_weights[0] * lgb_oof + best_weights[1] * xgb_oof + best_weights[2] * cat_oof\n",
    "fused_test_probs = best_weights[0] * lgb_test_probs + best_weights[1] * xgb_test_probs + best_weights[2] * cat_test_probs\n",
    "\n",
    "# 3. 优化阈值函数\n",
    "def optimize_thresholds(thresholds):\n",
    "    adjusted_probs = fused_oof / thresholds\n",
    "    preds = np.argmax(adjusted_probs, axis=1)\n",
    "    return -f1_score(train_y_converted, preds, average='macro')\n",
    "\n",
    "# 4. 优化阈值\n",
    "print(\"\\n为融合模型优化阈值...\")\n",
    "initial_thresholds = np.ones(n_classes)\n",
    "result = minimize(optimize_thresholds, initial_thresholds, \n",
    "                  method='Nelder-Mead', \n",
    "                  bounds=[(0.1, 10)] * n_classes)\n",
    "\n",
    "best_thresholds = result.x\n",
    "print(f\"最优阈值: {best_thresholds}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "450e6261",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# 5. 应用最优阈值\n",
    "adjusted_probs = fused_oof / best_thresholds\n",
    "best_preds_converted = np.argmax(adjusted_probs, axis=1)\n",
    "best_preds = best_preds_converted + 1\n",
    "best_f1 = f1_score(train_y, best_preds, average='macro')\n",
    "print(f\"融合模型使用最优阈值的Macro F1: {best_f1:.5f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "id": "fca3ef6b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "模型性能总结:\n",
      "LightGBM: 平均Macro F1 = 0.35500\n",
      "XGBoost: 平均Macro F1 = 0.41663\n",
      "CatBoost: 平均Macro F1 = 0.42310\n",
      "融合模型: Macro F1 = 0.43861 (提升: 0.01551)\n",
      "\n",
      "预测分布:\n",
      "4    13694\n",
      "2     4270\n",
      "3     3191\n",
      "1     2701\n",
      "Name: Target, dtype: int64\n",
      "\n",
      "提交文件已保存为: submission_fused_1013_212549.csv\n"
     ]
    }
   ],
   "source": [
    "# 6. 测试集预测\n",
    "adjusted_test_probs = fused_test_probs / best_thresholds\n",
    "test_preds_optimized_converted = np.argmax(adjusted_test_probs, axis=1)\n",
    "test_preds_optimized = test_preds_optimized_converted + 1\n",
    "\n",
    "# 7. 保存结果\n",
    "submission = pd.read_csv(\"./test.csv\")[['Id']]\n",
    "submission['Target'] = test_preds_optimized\n",
    "\n",
    "# 8. 输出模型性能\n",
    "print(\"\\n模型性能总结:\")\n",
    "print(f\"LightGBM: 平均Macro F1 = {lgb_f1:.5f}\")\n",
    "print(f\"XGBoost: 平均Macro F1 = {xgb_f1:.5f}\")\n",
    "print(f\"CatBoost: 平均Macro F1 = {cat_f1:.5f}\")\n",
    "print(f\"融合模型: Macro F1 = {best_f1:.5f} (提升: {best_f1 - max(lgb_f1, xgb_f1, cat_f1):.5f})\")\n",
    "\n",
    "# 9. 输出预测分布\n",
    "print(\"\\n预测分布:\")\n",
    "print(submission['Target'].value_counts())\n",
    "\n",
    "# 10. 保存结果\n",
    "from datetime import datetime\n",
    "current_time = datetime.now().strftime(\"%m%d_%H%M%S\")\n",
    "filename = f'submission_fused_{current_time}.csv'\n",
    "submission.to_csv(filename, index=False)\n",
    "print(f\"\\n提交文件已保存为: {filename}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d02e5727",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "81503193",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d1ee8f21",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1e4fc8a8",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "af0e9218",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Stacking堆叠融合"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "03e1b219",
   "metadata": {},
   "outputs": [],
   "source": [
    "# %%\n",
    "# 准备Stacking所需的数据结构\n",
    "n_classes = len(np.unique(train_y_converted))\n",
    "stacked_train = np.zeros((train_X.shape[0], n_classes * 3))  # 3个模型 * n_classes个概率\n",
    "stacked_test = np.zeros((test_X.shape[0], n_classes * 3))\n",
    "\n",
    "# 将基模型的预测结果存储到Stacking矩阵中\n",
    "stacked_train[:, :n_classes] = lgb_oof\n",
    "stacked_train[:, n_classes:2*n_classes] = xgb_oof\n",
    "stacked_train[:, 2*n_classes:] = cat_oof\n",
    "\n",
    "stacked_test[:, :n_classes] = lgb_test_probs\n",
    "stacked_test[:, n_classes:2*n_classes] = xgb_test_probs\n",
    "stacked_test[:, 2*n_classes:] = cat_test_probs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "c9824758",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "训练元模型 Fold 1\n",
      "Fold 1 Macro F1: 0.41159\n",
      "训练元模型 Fold 2\n",
      "Fold 2 Macro F1: 0.43615\n",
      "训练元模型 Fold 3\n",
      "Fold 3 Macro F1: 0.39270\n",
      "训练元模型 Fold 4\n",
      "Fold 4 Macro F1: 0.43079\n",
      "训练元模型 Fold 5\n",
      "Fold 5 Macro F1: 0.39420\n",
      "元模型平均 Macro F1: 0.41309\n"
     ]
    }
   ],
   "source": [
    "# %%\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.model_selection import StratifiedKFold\n",
    "from sklearn.metrics import f1_score\n",
    "\n",
    "# 使用逻辑回归作为元模型\n",
    "meta_model = LogisticRegression(\n",
    "    multi_class='multinomial',\n",
    "    solver='lbfgs',\n",
    "    max_iter=1000,\n",
    "    class_weight='balanced',\n",
    "    random_state=seed\n",
    ")\n",
    "\n",
    "# 使用5折交叉验证训练元模型\n",
    "kf = StratifiedKFold(n_splits=5, shuffle=True, random_state=seed)\n",
    "stacked_oof = np.zeros((train_X.shape[0], n_classes))\n",
    "stacked_test_probs = np.zeros((test_X.shape[0], n_classes))\n",
    "cv_scores = []\n",
    "\n",
    "for fold, (train_idx, val_idx) in enumerate(kf.split(stacked_train, train_y_converted)):\n",
    "    print(f'训练元模型 Fold {fold+1}')\n",
    "    \n",
    "    # 分割数据\n",
    "    X_train, X_val = stacked_train[train_idx], stacked_train[val_idx]\n",
    "    y_train, y_val = train_y_converted[train_idx], train_y_converted[val_idx]\n",
    "    \n",
    "    # 训练元模型\n",
    "    meta_model.fit(X_train, y_train)\n",
    "    \n",
    "    # 验证集预测\n",
    "    val_pred = meta_model.predict_proba(X_val)\n",
    "    stacked_oof[val_idx] = val_pred\n",
    "    \n",
    "    # 测试集预测（整个stacked_test）\n",
    "    test_pred = meta_model.predict_proba(stacked_test)\n",
    "    stacked_test_probs += test_pred / 5\n",
    "    \n",
    "    # 计算F1分数\n",
    "    val_pred_labels = np.argmax(val_pred, axis=1)\n",
    "    fold_f1 = f1_score(y_val, val_pred_labels, average='macro')\n",
    "    cv_scores.append(fold_f1)\n",
    "    print(f'Fold {fold+1} Macro F1: {fold_f1:.5f}')\n",
    "\n",
    "# 计算平均F1分数\n",
    "mean_cv_f1 = np.mean(cv_scores)\n",
    "print(f\"元模型平均 Macro F1: {mean_cv_f1:.5f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "c06a6c93",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "为Stacking模型优化阈值...\n",
      "最优阈值: [1.16537904 0.9632285  1.07464915 0.7866076 ]\n"
     ]
    }
   ],
   "source": [
    "# %%\n",
    "# 优化阈值函数\n",
    "def optimize_thresholds(thresholds):\n",
    "    adjusted_probs = stacked_oof / thresholds\n",
    "    preds = np.argmax(adjusted_probs, axis=1)\n",
    "    return -f1_score(train_y_converted, preds, average='macro')\n",
    "\n",
    "# 优化阈值\n",
    "print(\"\\n为Stacking模型优化阈值...\")\n",
    "initial_thresholds = np.ones(n_classes)\n",
    "result = minimize(optimize_thresholds, initial_thresholds, \n",
    "                  method='Nelder-Mead', \n",
    "                  bounds=[(0.1, 10)] * n_classes)\n",
    "\n",
    "best_thresholds = result.x\n",
    "print(f\"最优阈值: {best_thresholds}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "75d4a27d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Stacking模型使用最优阈值的 Macro F1: 0.43861\n",
      "\n",
      "模型性能总结:\n",
      "LightGBM: 平均 Macro F1 = 0.35500\n",
      "XGBoost: 平均 Macro F1 = 0.41663\n",
      "CatBoost: 平均 Macro F1 = 0.42310\n",
      "Stacking模型: Macro F1 = 0.43861\n",
      "\n",
      "预测分布:\n",
      "4    12884\n",
      "2     4488\n",
      "3     3428\n",
      "1     3056\n",
      "Name: Target, dtype: int64\n",
      "\n",
      "提交文件已保存为: submission_stacking_1013_203543.csv\n"
     ]
    }
   ],
   "source": [
    "# %%\n",
    "# 应用最优阈值\n",
    "adjusted_probs = stacked_oof / best_thresholds\n",
    "best_preds_converted = np.argmax(adjusted_probs, axis=1)\n",
    "best_preds = best_preds_converted + 1\n",
    "best_f1 = f1_score(train_y, best_preds, average='macro')\n",
    "print(f\"Stacking模型使用最优阈值的 Macro F1: {best_f1:.5f}\")\n",
    "\n",
    "# %%\n",
    "# 测试集预测\n",
    "adjusted_test_probs = stacked_test_probs / best_thresholds\n",
    "test_preds_optimized_converted = np.argmax(adjusted_test_probs, axis=1)\n",
    "test_preds_optimized = test_preds_optimized_converted + 1\n",
    "\n",
    "# %%\n",
    "# 保存结果\n",
    "submission = pd.read_csv(\"./test.csv\")[['Id']]\n",
    "submission['Target'] = test_preds_optimized\n",
    "\n",
    "# 输出模型性能\n",
    "print(\"\\n模型性能总结:\")\n",
    "print(f\"LightGBM: 平均 Macro F1 = {lgb_f1:.5f}\")\n",
    "print(f\"XGBoost: 平均 Macro F1 = {xgb_f1:.5f}\")\n",
    "print(f\"CatBoost: 平均 Macro F1 = {cat_f1:.5f}\")\n",
    "print(f\"Stacking模型: Macro F1 = {best_f1:.5f}\")\n",
    "\n",
    "# 输出预测分布\n",
    "print(\"\\n预测分布:\")\n",
    "print(submission['Target'].value_counts())\n",
    "\n",
    "# 保存结果\n",
    "from datetime import datetime\n",
    "current_time = datetime.now().strftime(\"%m%d_%H%M%S\")\n",
    "filename = f'submission_stacking_{current_time}.csv'\n",
    "submission.to_csv(filename, index=False)\n",
    "print(f\"\\n提交文件已保存为: {filename}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "59d404d2",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
